/**************************************************************************************************
	$Header: /pub/cvsroot/mydns/src/mydns/task.c,v 1.11 2002/07/29 07:11:08 bboy Exp $
	task.c: Routines for processing and dealing with tasks.

	Copyright (C) 2002  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "named.h"


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	DESCTASK
	Describe a task; used by error/warning output.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
char *
desctask(t)
	TASK *t;
{
	static char desc[1024];

	snprintf(desc, sizeof(desc), "[%llu] %s: %s %s", t->internal_id, inet_ntoa(t->addr.sin_addr),
		mydns_qtype_str(t->qtype), t->qname ? (char *)t->qname : "<NONE>");
	return (desc);
}
/*--- desctask() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TASK_INIT
	Allocates and initializes a new task, and returns a pointer to it.
	t = task_init(NEED_ZONE, SOCK_DGRAM, fd, &addr);
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
TASK *
task_init(status, fd, protocol, addr)
	taskstat_t status;			/* Initial status */
	int fd;							/* Associated file descriptor for socket */
	int protocol;					/* Protocol (SOCK_DGRAM or SOCK_STREAM) */
	struct sockaddr_in *addr;	/* Remote address */
{
	TASK *new;

	if (!(new = calloc(1, sizeof(TASK))))
		Err("calloc");
	new->status = status;
	new->fd = fd;
	new->protocol = protocol;
	memcpy(&new->addr, addr, sizeof(struct sockaddr_in));
	new->internal_id = Status.udp_requests + Status.tcp_requests;
	if (answer_then_quit && new->internal_id > answer_then_quit)
	{
		server_status();
		named_cleanup(SIGQUIT);
	}
	new->timeout = current_time + task_timeout;

	return (new);
}
/*--- task_init() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TASK_FREE
	Free the memory used by a task.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
task_free(t)
	TASK *t;
{
	register int n;

	if (!t)
		return;

	numlist_free(t->Cnames);
	if (t->query)
		free(t->query);
	for (n = 0; n < t->numNames; n++)
		free(t->Names[n]);
	if (t->numNames)
		free(t->Names);
	free(t->Offsets);
	free(t->qd);
	rrlist_free(&t->an);
	rrlist_free(&t->ns);
	rrlist_free(&t->ar);
	if (t->rdata)
		free(t->rdata);
	if (t->reply)
		free(t->reply);
	if (t->protocol == SOCK_STREAM && t->fd > -1)
	{
#if HAVE_SHUTDOWN
		if (t->qtype != DNS_T_AXFR)
			shutdown(t->fd, 2);
#endif
		close(t->fd);
	}
	free(t);
}
/*--- task_free() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TASK_INIT_HEADER
	Sets and/or clears header fields and values as necessary.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
task_init_header(t)
	TASK *t;
{
	t->hdr.qr = 1;					/* This is the response, not the query */
	t->hdr.ra = 0;					/* Recursion is NOT available (this may change in the future) */
	t->hdr.rcode = DNS_R_OK;	/* Assume success unless told otherwise */
}
/*--- task_init_header() ------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TASK_PROCESS
	Process the specified task, if possible.  Returns a pointer to the next task.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
inline TASK *
task_process(t)
	TASK *t;
{
	TASK *next;

	Assert(t);
	next = t->next;

	/* Process task if necessary */
	if (t->status == NEED_READ)
	{
		switch (t->protocol)
		{
			case SOCK_DGRAM:
				Warnx("%s: %s", desctask(t), _("invalid state for UDP query"));
				dequeue(Tasks, t, 1);
				return (next);

			case SOCK_STREAM:
				if (read_tcp_query(t) < 0)
				{
					dequeue(Tasks, t, 1);
					return (next);
				}
				break;

			default:
				Warnx("%s: %d: %s", desctask(t), t->protocol, _("unknown/unsupported protocol"));
				dequeue(Tasks, t, 1);
				return (next);
		}
	}

	if (t->status == NEED_ANSWER)
	{
		resolve(t, ANSWER, t->qtype, t->qname);
		build_reply(t, 1);
		t->status = NEED_WRITE;
		if (t->protocol == SOCK_STREAM)
			return (next);
	}

	if (t->status == NEED_WRITE)
	{
		switch (t->protocol)
		{
			case SOCK_DGRAM:
				write_udp_reply(t);
				break;

			case SOCK_STREAM:
				if (write_tcp_reply(t) < 0)
				{
					dequeue(Tasks, t, 1);
					return (next);
				}
				break;

			default:
				Warnx("%s: %d: %s", desctask(t), t->protocol, _("unknown/unsupported protocol"));
				dequeue(Tasks, t, 1);
				return (next);
		}
	}

	return (next);
}
/*--- task_process() ----------------------------------------------------------------------------*/

/* vi:set ts=3: */
/* NEED_PO */
